/*
 * $Id: QEADIFImporter.m 80 2009-07-05 22:34:32Z jon@bway.net $
 * Chalk
 *
 * Created 6/23/09
 * Copyright Jon Gordon
 * 
 * Permission is hereby granted, free of charge, to any person obtaining 
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including 
 * without limitation the rights to use, copy, modify, merge, publish, 
 * distribute, sublicense, and/or sell copies of the Software, and to 
 * permit persons to whom the Software is furnished to do so, subject to 
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#import "QEADIFImporter.h"

#import "QEADIF.h"

typedef enum {
    qeKeyColumnInt16 = 1,
    qeKeyColumnInt32,
    qeKeyColumnInt64,
    qeKeyColumnDecimal,
    qeKeyColumnDouble,
    qeKeyColumnFloat,
    qeKeyColumnString,
    qeKeyColumnBoolean,
    qeKeyColumnDate,
    qeKeyColumnBinaryData
} qeADIFColumnType;

@interface QEADIFImporter (private)
+ (void)initializeTables;
+ (void)initializeField2ColumnMap;
+ (void)initializeColumnTypeTable;
@end


static NSDictionary *field2ColumnMap;
static NSDictionary *columnTypeTable;
static NSSet *callsignColumns;
static NSCalendar *gregorianCalendar;


@implementation QEADIFImporter
#pragma mark LIFECYCLE
+ (void)initialize
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    static BOOL initialized = NO;
    
    if (!initialized) {
        NSLog(@"  initializing");
        initialized = YES;
        [QEADIFImporter initializeTables];
        callsignColumns = [[NSSet alloc] initWithObjects:@"callsign", nil];
        gregorianCalendar = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
        [gregorianCalendar setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
    } // if
} // initialize

- (id)init
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    self = [super init];
    
    if (nil != self) {
        tmpRecord = [[NSMutableDictionary alloc] init];
        preservedFields = [[NSMutableArray alloc] init];
    } // if
    
    return self;
} // init

- (void)dealloc
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    [tmpRecord release];
    [preservedFields release];
    [super dealloc];
} // dealloc


#pragma mark OPERATIONS
- (NSDictionary *)importADIFRecord:(NSArray *)record
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    return [NSDictionary dictionaryWithDictionary:[self importADIFRecord:record intoObject:nil]];
} // importADIFRecord:

- (id)importADIFRecord:(NSArray *)record intoObject:(id)dest
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    [tmpRecord removeAllObjects];
    [preservedFields removeAllObjects];
    
    NSMutableSet *dateColumns = [[NSMutableSet alloc] init];
    
    if (nil == record) {
        return nil;
    } // if
    
    id result = dest;
    if (nil == result) {
        NSLog(@"  creating new mutable dictionary to hold result");
        result = [[[NSMutableDictionary alloc] initWithCapacity:[record count]] autorelease];
    } // if
    
    for (QEADIFDataField *field in record) {
        NSLog(@"  importing field: %@", field);
        NSString *columnName = [field2ColumnMap objectForKey:[field fieldName]];
        if (nil == columnName) {
            NSLog(@"  field name \"%@\" does not map to a column", [field fieldName]);
            [preservedFields addObject:field];
        } else {
            NSLog(@"  mapping to column \"%@\"", columnName);
            switch ([[columnTypeTable objectForKey:columnName] integerValue]) {
                case qeKeyColumnInt16:
                case qeKeyColumnInt32:
                case qeKeyColumnInt64:
                    [tmpRecord setObject:[NSNumber numberWithInt:[[field fieldValue] integerValue]] forKey:columnName];
                    break;
                case qeKeyColumnDecimal:
                    [tmpRecord setObject:[[field fieldValue] decimalValue] forKey:columnName];
                    break;
                case qeKeyColumnDouble:
                    [tmpRecord setObject:[NSNumber numberWithDouble:[[[field fieldValue] decimalValue] doubleValue]]
                                  forKey:columnName];
                    break;
                case qeKeyColumnFloat:
                    [tmpRecord setObject:[NSNumber numberWithFloat:[[[field fieldValue] decimalValue] floatValue]]
                                  forKey:columnName];
                    break;
                case qeKeyColumnString:
                    if (nil == [callsignColumns member:columnName]) {
                        [tmpRecord setObject:[[field fieldValue] stringValue] forKey:columnName];
                    } else {
                        [tmpRecord setObject:[[[field fieldValue] stringValue] uppercaseString] forKey:columnName];
                    } // else
                    break;
                case qeKeyColumnBoolean:
                    [tmpRecord setObject:[NSNumber numberWithBool:[[field fieldValue] boolValue]]
                                  forKey:columnName];
                    break;
                case qeKeyColumnBinaryData:
                    [tmpRecord setObject:[[field fieldValue] value] forKey:columnName];
                    break;
                case qeKeyColumnDate:
                    if (nil == [dateColumns member:columnName]) {
                        [dateColumns addObject:columnName];
                        [tmpRecord setObject:[[[[field fieldValue] componentValue] copy] autorelease]
                                      forKey:columnName];
                    } else {
                        NSDateComponents *c = [tmpRecord objectForKey:columnName];
                        NSDateComponents *newC = [[field fieldValue] componentValue];
                        if (qeADIFTypeDate == [QEADIFValue typeForFieldName:[field fieldName]]) {
                            [c setYear:[newC year]];
                            [c setMonth:[newC month]];
                            [c setDay:[newC day]];
                        } else if (qeADIFTypeTime == [QEADIFValue typeForFieldName:[field fieldName]]) {
                            [c setHour:[newC hour]];
                            [c setMinute:[newC minute]];
                            [c setSecond:[newC second]];
                        } else {
                            // CAN'T HAPPEN
                            NSLog(@"  ERROR: trying to store field \"%@\" into column \"%@\"; field isn't date or time, but column requires components",
                                  [field fieldName],
                                  columnName);
                        } //else
                    } // else
                    break;
                default:
                    NSLog(@"  no column type known for column \"%@\"; treating as a string",
                          columnName);
                    [tmpRecord setObject:[[field fieldValue] stringValue] forKey:columnName];
                    break;
            } // switch
        } // else
    } // for
    
    for (NSString *dateColumnName in dateColumns) {
        NSLog(@"  converting date column \"%@\"", dateColumnName);
        NSDateComponents *c = [tmpRecord objectForKey:dateColumnName];
        [tmpRecord setObject:[gregorianCalendar dateFromComponents:c] forKey:dateColumnName];
    } // for
    
    if (0 < [preservedFields count]) {
        NSMutableString *preservedFieldText = [[NSMutableString alloc] init];
        for (QEADIFDataField *preservedField in preservedFields) {
            [preservedFieldText appendString:[preservedField description]];
        } // for
        [tmpRecord setValue:preservedFieldText forKey:@"preservedFields"];
        [preservedFieldText release];
    } // if
    
    NSArray *columnNames = [tmpRecord allKeys];
    for (id columnName in columnNames) {
        NSLog(@"  saving value \"%@\" for column: %@", [tmpRecord valueForKey:columnName], columnName);
        [result setValue:[tmpRecord valueForKey:columnName] forKey:columnName];
    } // for
    NSLog(@"  all values stored, returning: %@", result);
    
    return result;
} // importADIFRecord:intoObject:


#pragma mark DATA MANAGEMENT
+ (BOOL)accessInstanceVariablesDirectly
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    return NO;
} // accessInstanceVariablesDirectly

+ (void)initializeTables
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    [QEADIFImporter initializeField2ColumnMap];
    [QEADIFImporter initializeColumnTypeTable];
} // initializeTables

+ (void)initializeField2ColumnMap
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
    
    // Key: ADIF field name; Object: column name
    [tmp setObject:@"band"     forKey:@"BAND"];
    [tmp setObject:@"callsign" forKey:@"CALL"];
    [tmp setObject:@"comment"  forKey:@"COMMENT"];
    [tmp setObject:@"freq"     forKey:@"FREQ"];
    [tmp setObject:@"mode"     forKey:@"MODE"];
    [tmp setObject:@"qso_date" forKey:@"QSO_DATE"];
    [tmp setObject:@"rst_rcvd" forKey:@"RST_RCVD"];
    [tmp setObject:@"rst_sent" forKey:@"RST_SENT"];
    [tmp setObject:@"qso_date" forKey:@"TIME_ON"];
    
    field2ColumnMap = [[NSDictionary alloc] initWithDictionary:tmp];
    [tmp release];
} // initializeField2ColumnMap

+ (void)initializeColumnTypeTable
{
    NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
    
    // Key: column name; Object: column type
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnString]  forKey:@"band"];
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnString]  forKey:@"callsign"];
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnString]  forKey:@"comment"];
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnDecimal] forKey:@"freq"];
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnString]  forKey:@"mode"];
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnDate]    forKey:@"qso_date"];
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnString]  forKey:@"rst_rcvd"];
    [tmp setObject:[NSNumber numberWithInt:qeKeyColumnString]  forKey:@"rst_sent"];
    
    columnTypeTable = [[NSDictionary alloc] initWithDictionary:tmp];
    [tmp release];
} // initializeColumnTypeTable
@end
